package com.google.code.googlefight.fetch;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.Locale;

import org.json.JSONException;
import org.json.JSONObject;

/**
 * Hilfsklasse für die Google-Suchanfragen-API. Da die Suchanfrage je nach
 * Sprache/Lokalisierung unterschiedlich ausfällt, lässt sich diese beim
 * Initialisieren der Klasse übergeben.
 * 
 * Diese Klasse nutzt die veraltete Search-API.
 * 
 * @see <a
 *      href="https://developers.google.com/web-search/docs/reference">Dokumentation
 *      der Google-Search-API</a>
 * @author Hans
 */
public class GoogleSearchImplAPI implements GoogleSearch {
	private static final String HTTP_HEADER_KEY_USER_AGENT = "User-Agent";
	private static final String HTTP_HEADER_KEY_REFERER = "Referer";
	private static final String HTTP_HEADER_KEY_ACCEPT_LANGUAGE = "Accept-Language";

	/**
	 * User-Agent für die Anfrage, hier: Chrome 30 auf Windows
	 */
	private static final String HTTP_HEADER_VALUE_USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36";

	/**
	 * Referer für die Anfrage, hier: code.google.com
	 */
	private static final String HTTP_HEADER_VALUE_REFERER = "https://code.google.com/p/googlefight/";

	/**
	 * rsz=1 heisst: nur 1 Suchergebnis zurückliefern -> kürzere Abfrage-Dauer
	 */
	private static final String BASE_URL = "http://ajax.googleapis.com/ajax/services/search/web?v=1.0&rsz=1&q=";

	/**
	 * Dieser String wird an einen Accept-Language-Header-Wert angehangen. Als
	 * Sprach-Alternative wird damit Englisch gesetzt.
	 */
	private static final String ACCEPT_LANGUAGE_SUFFIX = "en-US;q=0.6,en;q=0.4";

	/**
	 * Standard-Zeichensatz fürs Lesen und Schreiben: UTF-8
	 */
	private static final String UTF_8 = "utf-8";

	private static final String JSON_RESPONSE_DATA = "responseData";
	private static final String JSON_CURSOR = "cursor";
	private static final String JSON_ESTIMATED_RESULT_COUNT = "estimatedResultCount";

	/**
	 * Initialisiert einen neuen Helper mit US-Amerikanischer Sprache.
	 */
	public GoogleSearchImplAPI() {
	}

	/**
	 * Gibt die Anzahl der Ergebnisse für das Suchwort zurück. Die Sprache kann
	 * selbst übergeben werden.
	 * 
	 * @param query
	 *            Suchwort
	 * @param locale
	 *            Sprache
	 * @return Anzahl der Suchergebnisse
	 * @throws IOException
	 *             Netzwerk-Fehler
	 * @throws JSONException
	 *             Parsing-Fehler
	 */
	public long getSearchResults(String query, Locale locale)
			throws GoogleSearchException {
		if (locale == null)
			throw new NullPointerException("Locale is null");
		try {

			URL url = new URL(BASE_URL + URLEncoder.encode(query, UTF_8));
			URLConnection conn = url.openConnection();

			conn.setRequestProperty(HTTP_HEADER_KEY_USER_AGENT,
					HTTP_HEADER_VALUE_USER_AGENT);
			conn.setRequestProperty(HTTP_HEADER_KEY_REFERER,
					HTTP_HEADER_VALUE_REFERER);
			conn.setRequestProperty(HTTP_HEADER_KEY_ACCEPT_LANGUAGE,
					getAcceptLanguageString(locale));
			conn.connect();

			Reader reader = new InputStreamReader(conn.getInputStream(), UTF_8);
			StringBuilder string = new StringBuilder();
			char[] buf = new char[1024];
			int read;

			while ((read = reader.read(buf)) != -1) {
				string.append(buf, 0, read);
			}

			reader.close();

			JSONObject obj = new JSONObject(string.toString());

			return Long.parseLong(obj.getJSONObject(JSON_RESPONSE_DATA)
					.getJSONObject(JSON_CURSOR)
					.getString(JSON_ESTIMATED_RESULT_COUNT));
		} catch (Exception e) {
			throw new GoogleSearchException(e);
		}
	}

	/**
	 * Gibt einen String zurück, der als Wert im Accept-Language-Header
	 * verwendet werden kann. <br/>
	 * Beispiel: {@code de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4}
	 * 
	 * @param locale
	 *            Sprache
	 * @return Wert für Accept-Language
	 */
	public static String getAcceptLanguageString(Locale locale) {
		StringBuilder str = new StringBuilder();
		if (locale.getLanguage().length() != 0) {
			if (locale.getCountry().length() != 0) {
				str.append(locale.getLanguage()).append('-')
						.append(locale.getCountry()).append(',');
			}
			str.append(locale.getLanguage()).append(";q=0.8,");
		}
		str.append(ACCEPT_LANGUAGE_SUFFIX);
		return str.toString();
	}
}